// src/pages/test/useRefTest/index.tsx - useRef 调用子组件方法详解页面
import React, { useState, useRef, useImperativeHandle, forwardRef } from 'react';
import { Card, Button, Space, Typography, Divider, Input } from 'antd';

const { Title, Paragraph } = Typography;

/**
 * 子组件 - 带有暴露方法的组件
 * 使用 forwardRef 和 useImperativeHandle 来暴露方法给父组件调用
 */
interface ChildComponentHandles {
  focus: () => void;
  reset: () => void;
  getValue: () => string;
}

const ChildComponent = forwardRef<ChildComponentHandles>((_props, ref) => {
  const [value, setValue] = useState<string>('');

  // 使用 useImperativeHandle 暴露方法给父组件
  useImperativeHandle(ref, () => ({
    // 聚焦到输入框
    focus: () => {
      console.log('子组件的 focus 方法被调用');
    },
    
    // 重置输入框
    reset: () => {
      console.log('子组件的 reset 方法被调用');
      setValue('');
    },
    
    // 获取输入框的值
    getValue: () => {
      console.log('子组件的 getValue 方法被调用');
      return value;
    }
  }));

  return (
    <div style={{ padding: '20px', border: '1px solid #ddd', borderRadius: '4px' }}>
      <h3>子组件</h3>
      <Space>
        <span>输入框:</span>
        <Input 
          value={value} 
          onChange={(e) => setValue(e.target.value)} 
          placeholder="请输入内容"
          style={{ width: 200 }}
        />
      </Space>
      <div style={{ marginTop: '10px' }}>
        <Paragraph type="secondary">
          这个组件暴露了三个方法给父组件调用: focus(), reset(), getValue()
        </Paragraph>
      </div>
    </div>
  );
});

/**
 * 子组件 - 计数器组件
 */
interface CounterComponentHandles {
  increment: () => void;
  decrement: () => void;
  reset: () => void;
}

const CounterComponent = forwardRef<CounterComponentHandles>((_props, ref) => {
  const [count, setCount] = useState<number>(0);

  // 暴露计数器相关方法
  useImperativeHandle(ref, () => ({
    increment: () => {
      setCount(prev => prev + 1);
      console.log('计数器增加');
    },
    
    decrement: () => {
      setCount(prev => prev - 1);
      console.log('计数器减少');
    },
    
    reset: () => {
      setCount(0);
      console.log('计数器重置');
    }
  }));

  return (
    <div style={{ padding: '20px', border: '1px solid #ddd', borderRadius: '4px', marginTop: '10px' }}>
      <h3>计数器子组件</h3>
      <Paragraph>当前计数: <strong>{count}</strong></Paragraph>
      <Paragraph type="secondary">
        这个组件暴露了三个方法给父组件调用: increment(), decrement(), reset()
      </Paragraph>
    </div>
  );
});

const UseRefTestPage: React.FC = () => {
  // 创建 ref 用于调用子组件方法
  const childRef = useRef<ChildComponentHandles>(null);
  const counterRef = useRef<CounterComponentHandles>(null);
  
  // 父组件状态
  const [parentMessage, setParentMessage] = useState<string>('');

  /**
   * 调用子组件的 focus 方法
   */
  const handleFocusChild = () => {
    if (childRef.current) {
      childRef.current.focus();
    }
  };

  /**
   * 调用子组件的 reset 方法
   */
  const handleResetChild = () => {
    if (childRef.current) {
      childRef.current.reset();
    }
  };

  /**
   * 调用子组件的 getValue 方法
   */
  const handleGetValueFromChild = () => {
    if (childRef.current) {
      const value = childRef.current.getValue();
      setParentMessage(`从子组件获取的值: ${value}`);
    }
  };

  /**
   * 调用计数器组件的方法
   */
  const handleIncrement = () => {
    if (counterRef.current) {
      counterRef.current.increment();
    }
  };

  const handleDecrement = () => {
    if (counterRef.current) {
      counterRef.current.decrement();
    }
  };

  const handleResetCounter = () => {
    if (counterRef.current) {
      counterRef.current.reset();
    }
  };

  return (
    <Card title="useRef 调用子组件方法详解" style={{ minHeight: '100%' }}>
      <Typography>
        <Title level={2}>useRef 调用子组件方法</Title>
        <Paragraph>
          在 React 中，父组件可以通过 useRef 来调用子组件暴露的方法，这类似于 Vue 中通过 ref 调用子组件方法的功能。
          这种方式需要子组件使用 forwardRef 和 useImperativeHandle 来实现。
        </Paragraph>
        
        <Title level={3}>实现方式</Title>
        <Paragraph>
          1. 子组件使用 <code>forwardRef</code> 包装，接收 ref 参数
        </Paragraph>
        <Paragraph>
          2. 使用 <code>useImperativeHandle</code> Hook 定义要暴露给父组件的方法
        </Paragraph>
        <Paragraph>
          3. 父组件创建 ref 并通过 ref 属性传递给子组件
        </Paragraph>
        <Paragraph>
          4. 父组件通过 ref.current 调用子组件暴露的方法
        </Paragraph>
        
        <Title level={3}>基础语法</Title>
        <Paragraph>
          <pre>
            {`// 子组件中
const ChildComponent = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    methodName() {
      // 方法实现
    }
  }));
});

// 父组件中
const childRef = useRef(null);
<ChildComponent ref={childRef} />
childRef.current.methodName();`}
          </pre>
        </Paragraph>
      </Typography>
      
      <Divider />
      
      {/* 示例区域 */}
      <Title level={4}>1. 基础示例</Title>
      <Space direction="vertical" style={{ width: '100%' }}>
        <Space>
          <Button onClick={handleFocusChild}>
            调用子组件 focus 方法
          </Button>
          <Button onClick={handleResetChild}>
            调用子组件 reset 方法
          </Button>
          <Button onClick={handleGetValueFromChild}>
            调用子组件 getValue 方法
          </Button>
        </Space>
        
        {/* 子组件 */}
        <ChildComponent ref={childRef} />
        
        {/* 显示从子组件获取的值 */}
        {parentMessage && (
          <Paragraph>
            <strong>父组件收到的消息:</strong> {parentMessage}
          </Paragraph>
        )}
      </Space>
      
      <Divider />
      
      {/* 计数器示例 */}
      <Title level={4}>2. 计数器示例</Title>
      <Space direction="vertical" style={{ width: '100%' }}>
        <Space>
          <Button onClick={handleIncrement}>
            增加计数
          </Button>
          <Button onClick={handleDecrement}>
            减少计数
          </Button>
          <Button onClick={handleResetCounter}>
            重置计数
          </Button>
        </Space>
        
        {/* 计数器子组件 */}
        <CounterComponent ref={counterRef} />
      </Space>
      
      <Divider />
      
      <Title level={3}>使用场景</Title>
      <Paragraph>
        <ul>
          <li>
            <strong>表单操作：</strong>
            父组件需要直接操作子组件中的表单元素，如聚焦、清空、验证等
          </li>
          <li>
            <strong>动画控制：</strong>
            父组件需要控制子组件中的动画播放、暂停等
          </li>
          <li>
            <strong>媒体控制：</strong>
            父组件需要控制子组件中的视频、音频播放器
          </li>
          <li>
            <strong>组件交互：</strong>
            父组件需要调用子组件的特定功能方法
          </li>
        </ul>
      </Paragraph>
      
      <Title level={3}>注意事项</Title>
      <Paragraph>
        <ul>
          <li>
            <strong>避免过度使用：</strong>
            这种方式打破了 React 数据流的单向性，应该谨慎使用，优先考虑通过 props 和回调函数进行通信
          </li>
          <li>
            <strong>类型安全：</strong>
            在 TypeScript 中，应该明确定义 ref 的类型以获得类型检查和自动补全
          </li>
          <li>
            <strong>空值检查：</strong>
            调用子组件方法前应检查 ref.current 是否存在
          </li>
          <li>
            <strong>函数组件专用：</strong>
            这种方式只适用于函数组件，类组件有其他方式实现类似功能
          </li>
        </ul>
      </Paragraph>
      
      <Title level={3}>与 Vue 的对比</Title>
      <Paragraph>
        <ul>
          <li>
            <strong>Vue:</strong>
            通过 ref 属性直接访问子组件实例及其方法
          </li>
          <li>
            <strong>React:</strong>
            需要子组件使用 forwardRef 和 useImperativeHandle 显式暴露方法
          </li>
          <li>
            <strong>设计理念：</strong>
            React 的方式更加显式和可控，Vue 的方式更加直接但可能破坏组件封装性
          </li>
        </ul>
      </Paragraph>
    </Card>
  );
};

export default UseRefTestPage;